#include <iostream>
#include <vector>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sstream>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h> 
#include <errno.h>

using namespace std;

char pwd[1024];
char myenv[1024];
char* username;//用户名
char Identifier;//用户标识符
int lastcode=0;//最后一个结束的子进程的ID。



#define NODE -1
#define IN_RDIR 0
#define OUT_RDIR 1
#define APPEND_RDIR 2

char filename[1024];//用于重定向的文件名
int rdir=NODE;//文件打开的模式
char _color[]="--color";

//获取用户名
void getIdentifier()
{   
    username=getlogin();
    if(username==nullptr)
    {
        Identifier='?';return;
    } 
    if(strcmp(username,"root")==0) Identifier='#';
    else Identifier='$';
}

//得到pwd路径
void getpwd()
{
    getcwd(pwd,sizeof(pwd));
}

//输入文本框前缀
 void interact()
 {
    getpwd();
    printf("[%s@VM-4-14-ubuntu]%s%c ",username,pwd,Identifier);
 }

 //判断用户输入的字符串
int splitstring(vector<string>& argv)
{
    rdir=NODE;
    filename[0]='\0';
    string tmp;
    //cin>>tmp;
    getline(cin,tmp);
    tmp+='\0';//由于是C++的string，不会默认+'\0’，但C的字符串默认以‘\0’结尾
    //char *pos
    size_t pos=0;
    for(int i=0;i<tmp.size();i++)
    {
        if(tmp[i]=='>')
        {
            //如果是一个合法字符串命令，i+2必须<tmp.size（）-1,因为tmp.size（）-1是'\0';
            if(i+2>=tmp.size())//等于也不行，假设最后输入内容为 ‘>‘ ’ ‘ ’\0‘，说明重定向后面没有输入任何有效内容，无法构成重定向因此命令是无效的。
            {
                //printf("command error\n");
                return 0;
            }
            if(tmp[i+1]=='>')//追加重定向
            {
                if(i+3>=tmp.size())  return 0;
                tmp[i]='\n';
                strcpy(filename,(tmp.c_str()+i+3));
                rdir=APPEND_RDIR;
                tmp.resize(i);
                //if(filename==nullptr)   return 0;
                //int fd=open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
                //dup2(fd,1);
            }
            else//输出重定向
            {
                //if(i+3>tmp.size())  return 0;
                tmp[i]='\n';
                strcpy(filename,(tmp.c_str()+i+2));
                rdir=OUT_RDIR;
                tmp.resize(i);
            }
        }
        if(tmp[i]=='<')
        {
            if(i+2>=tmp.size())  return 0;
            tmp[i]='\n';
            strcpy(filename,(tmp.c_str()+i+2));
            rdir=IN_RDIR;
            tmp.resize(i);
        }
    }
    istringstream iss(tmp);
    string token;
    argv.clear();
    while(iss>>token)
    {
        argv.push_back(token);
    }
    if(argv.empty()) return 0;
    
   
    return argv.size();
}


//对内建命令进行处理和判断
int buildCommand(char* c_argv[],int argc)
{    
    //0表示不是内建命令，如果是非0表示是内建命令。
    if(strcmp(c_argv[0],"cd")==0)
    {
        chdir(c_argv[1]);
        getpwd();
        //cd实现的逻辑，稍后处理；
        return 1;
    }
    else if(strcmp(c_argv[0],"echo")==0)
    {
        if(strcmp(c_argv[1], "$?") == 0)
        {
            printf("%d\n", lastcode);
            lastcode=0;
        }
        else if(*c_argv[1] == '$'){
            char *val = getenv(c_argv[1]+1);
            if(val) printf("%s\n", val);
        }
        else{
            printf("%s\n", c_argv[1]);
        }   
        return 1;
    }
    else if(strcmp(c_argv[0],"export")==0)
    {
        strcpy(myenv,c_argv[1]);
        putenv(myenv);
        return 1;
    }
    
    if(strcmp(c_argv[0],"ls")==0)
    {
        
        c_argv[argc++]=_color;
        c_argv[argc]=nullptr;
    }

    return 0;
}


//处理非内建命令
void NormalExcute(char* c_argv[])
{

    // if(access(c_argv[0],F_OK)==-1)
    // {
    //     printf("no such instruction '%s'\n",c_argv[0]);
    //     return;
    // }
    // if(access(c_argv[0],X_OK)==-1)
    // {
    //     printf("No execution permission\n");
    //     return;
    // }

    auto pid=fork();
    if(pid<0)
    {
        perror("fork error");
        return;
    }
    else if(pid==0)
    {
        if(rdir==IN_RDIR)
        {
            int fd=open(filename,O_RDONLY);
            dup2(fd,0);
            close(fd);
        }
        else if(rdir==OUT_RDIR)
        {
            int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            dup2(fd,1);
            close(fd);
        }
        else if(rdir==APPEND_RDIR)
        {
            int fd=open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
            dup2(fd,1);
            close(fd);
        }
        else;
        execvp(c_argv[0],c_argv);
            //命令的合法化检查
        switch(errno) 
        {
            case ENOENT:  // 命令不存在
                printf("%s: command not found\n", c_argv[0]);
                break;
            case EACCES:  // 无权限
                printf("%s: permission denied\n", c_argv[0]);
                break;
            default:
                perror("execvp failed");
        }
        exit(44);
    }
    else
    {
        int status;
        auto rid=waitpid(pid,&status,0);
        if(rid==pid)
        {
            lastcode=WEXITSTATUS(status);
        }
    }
    rdir=NODE;
}



int main()
{
    getIdentifier();//获取用户名标识符
    while(1)
    {
        //1.创建互动前缀 
        interact();

        //2.获取字符串
        vector<string> argv;
        int argc=splitstring(argv);
        // if(argc)
        // {
        //     //scf!=0,说明捕获到了某些指令;
        //     for(auto& it:argv)
        //         cout<<it<<" ";
        //     cout<<endl;
        // }
        if(!argc) continue;//scf=0,说明用户什么也没输入直接回车了
            //char* c_argv[32];
        char* c_argv[argv.size()+1];
        int i=0;
        for(;i<argv.size();i++)
        {
            //c_argv[i]=&argv[i][0];不推荐的写法，这样做会导致外部元素直接访问string底层，是危险做法
            c_argv[i]=const_cast<char*>(argv[i].data());//c+17起支持的语法，拷贝一份字符串，对原string没有影响；
        }
        c_argv[i]=nullptr;
        //3.判断指令是不是内建指令
        int n=buildCommand(c_argv,argc);

        //4.判断指令是不是普通指令
        if(!n)
            NormalExcute(c_argv);
    }

    cout<<"END!!!!!!!"<<endl;
    return 0;
}
